<!--
 * @Description: 
 * @Version: 2.0
 * @Autor: zss
 * @Date: 2024-07-19 08:46:16
 * @LastEditors: swc shanwenchao12138@163.com
 * @LastEditTime: 2025-02-21 13:19:45
-->
<template>
  <div class="container">
    <div class="left-panel" :style="navLeftPanelStyle">
      <div class="nav-title">
        <div class="logo-elip" :style="navZoomBtnStyle" @click="navZoom">
          <Icon custom="wy-icon wy-Hamburg" />
        </div>
        <div class="logo">
          <span>{{ $srv.getI18nByValue($srv.lexicon.headTitle) }}</span>
        </div>
      </div>
      <div ref="navLeftPanel" class="nav-content">
        <ul>
          <li v-for="(item, idx) of navConfig" :key="idx">
            <root-nav :item="item" :compBoxInfo="GT_compBoxInfo"></root-nav>
          </li>
        </ul>
      </div>
    </div>
    <div class="right-panel">
      <rightTopMenu
        @selectFunc="selectFunc"
        @addTab="addTab"
        :historyConfig="historyConfig"
        :navConfig="navConfig"
        :funcConfig="funcConfig"
        :compBoxInfo="GT_compBoxInfo"
      ></rightTopMenu>
      <div class="main-panel">
        <Tabs
          ref="mainTabs"
          :animated="false"
          type="card"
          v-model="editableTabsValue"
          @on-tab-remove="removeTab"
          @on-click="changeTab"
          name="mainTab"
          class="main-panel-tabs"
          @on-contextmenu="handleContextMenu"
          :before-remove="beforeRemoveTab"
        >
          <TabPane
            v-for="(item, idx) in editableTabs"
            :closable="isIndex(item.field)"
            :key="item.field"
            :label="tabsTitle(item.field, item.title)"
            :name="item.field"
            tab="mainTab"
            :index="idx"
            :class="[item.field === editableTabsValue ? 'active' : '']"
            :context-menu="isIndex(item.field)"
          >
            <o-layout
              :config="layoutConfig[item.field]"
              v-if="item.field !== 'index'"
              @open-fun="openFun(item.field, $event)"
            ></o-layout>

            <home-page v-else />
          </TabPane>

          <template slot="contextMenu">
            <DropdownItem @click.native="tabCloesOther">
              <!-- 关闭其他页签 -->
              {{ $srv.getI18nByPath('menu.closeothertabs') }}
            </DropdownItem>
            <DropdownItem @click.native="tabCloesLeft">
              <!-- 关闭左侧页签 -->
              {{ $srv.getI18nByPath('menu.closethelefttab') }}
            </DropdownItem>
            <DropdownItem @click.native="tabCloesRight">
              <!-- 关闭右侧页签 -->
              {{ $srv.getI18nByPath('menu.closetherighttab') }}
            </DropdownItem>
          </template>
        </Tabs>
      </div>
    </div>
    <!-- 移到app.vue -->
    <!-- <template v-for="(modal, idx) of modalList">
      <component
        :is="modal.name"
        :key="idx"
        v-bind="{ config: modal.params, id: modal.moduleID }"
      ></component>
    </template> -->
    <!-- 版本信息 -->
    <div class="version-box" v-if="versionShow">
      <div class="info-container y-scroll">
        <div class="version-title">
          {{ $srv.getI18nByValue('系统版本信息') }}
        </div>
        <div class="info-scoll">
          <div class="version-item">
            <span>{{ $srv.getI18nByValue('系统平台') }}:</span>
            <div>
              <span>{{ versionInfo.platform }}</span>
            </div>
          </div>
          <div class="version-item">
            <span>{{ $srv.getI18nByValue('Web版本') }}:</span>
            <div>
              <span>{{ versionInfo.web }}</span>
            </div>
          </div>
          <div class="version-item">
            <span>{{ $srv.getI18nByValue('微服务') }}:</span>
            <div>
              <div
                class="item"
                v-for="(item, idx) in versionInfo.services"
                :key="idx"
              >
                <span>{{ item.Name.split('.')[0] }}:</span>
                <span>{{ item.Ver }}</span>
              </div>
            </div>
          </div>
        </div>
        <div class="version-footer">
          <Button type="primary" @click="versionShow = !versionShow">{{
            $srv.getI18nByValue('确定')
          }}</Button>
        </div>
      </div>
    </div>
    <!-- 修改密码 -->
    <div class="password-box" v-if="passwordShow">
      <Form ref="chgPwdForms" :model="chgPwdForm" :rules="chgPwdRules" inline>
        <!-- <FormItem prop="oldPwd" label="旧密码:"> -->
        <FormItem
          prop="oldPwd"
          :label="$srv.getI18nByPath('oPswChange.oldPassword') + ':'"
        >
          <i-input
            :type="flag.oldPwd ? 'text' : 'password'"
            v-model="chgPwdForm.oldPwd"
            autocomplete="off"
            size="small"
            clearable
            :placeholder="$srv.getI18nByPath('oPswChange.enteroldpassword')"
          >
            <Icon type="ios-lock-outline" slot="prepend"></Icon>
            <Icon
              :class="flag.oldPwd ? 'ios-eye-outline' : 'ios-eye-off-outline'"
              :type="flag.oldPwd ? 'ios-eye-outline' : 'ios-eye-off-outline'"
              slot="append"
              @click="flag.oldPwd = !flag.oldPwd"
            ></Icon>
          </i-input>
        </FormItem>
        <!-- <FormItem prop="newPwd" label="新密码:"> -->
        <FormItem
          prop="newPwd"
          :label="$srv.getI18nByPath('oPswChange.newpassword') + ':'"
        >
          <i-input
            :type="flag.newPwd ? 'text' : 'password'"
            v-model="chgPwdForm.newPwd"
            autocomplete="off"
            size="small"
            clearable
            :placeholder="$srv.getI18nByPath('oPswChange.enternewpassword')"
          >
            <Icon type="ios-lock-outline" slot="prepend"></Icon>
            <Icon
              :class="flag.newPwd ? 'ios-eye-outline' : 'ios-eye-off-outline'"
              :type="flag.newPwd ? 'ios-eye-outline' : 'ios-eye-off-outline'"
              slot="append"
              @click="flag.newPwd = !flag.newPwd"
            ></Icon>
          </i-input>
        </FormItem>
        <!-- <FormItem prop="confirmPwd" label="请确认新密码:"> -->
        <FormItem
          prop="confirmPwd"
          :label="$srv.getI18nByPath('oPswChange.confirmnewpassword') + ':'"
        >
          <i-input
            :type="flag.confirmPwd ? 'text' : 'password'"
            v-model="chgPwdForm.confirmPwd"
            autocomplete="off"
            size="small"
            clearable
            :placeholder="$srv.getI18nByPath('oPswChange.confirmnewpassword')"
          >
            <Icon type="ios-lock-outline" slot="prepend"></Icon>
            <Icon
              :class="
                flag.confirmPwd ? 'ios-eye-outline' : 'ios-eye-off-outline'
              "
              :type="
                flag.confirmPwd ? 'ios-eye-outline' : 'ios-eye-off-outline'
              "
              slot="append"
              @click="flag.confirmPwd = !flag.confirmPwd"
            ></Icon>
          </i-input>
        </FormItem>
      </Form>
      <div class="chgPwd-button">
        <Button size="small" @click="chgOk">{{
          $srv.getI18nByPath('others.ok')
        }}</Button>
        <Button size="small" @click="chgCanel">{{
          $srv.getI18nByPath('others.cancel')
        }}</Button>
      </div>
    </div>
    <msg-tips ref="msgTips" v-model="tipsShow" :compBoxInfo="GT_compBoxInfo" />
  </div>
</template>

<script lang="ts">
import { setTheme } from '@/config/theme.config';
import { Mutation } from 'vuex-class';
import {
  I_funcConf,
  N_home,
  T_home,
  T_srv,
  Tsrv,
  ifs,
  ts,
  tvs,
  vue
} from './config';
import msgTips from './msgTips.vue';
import rightTopMenu from './rightTopMenu.vue'; // 侧边导航
import RootNav from './rootNav.vue'; // 侧边导航
// import homePage from './homePage.vue'; // 侧边导航

@T_srv.comp({
  components: {
    RootNav,
    rightTopMenu,
    msgTips
    // homePage
  }
})
export default class Home extends vue {
  @Mutation('ADD_SYSMSGTIPS') addSysTips;

  @T_srv.getter('SysMsgTips') sysMsgTips;

  @T_srv.getter('SysMsgCount') sysMsgCount;

  // @T_srv.inject({ default: () => {} }) addTabFunc = this.addTab;
  @T_srv.provide() addTabFunc = this.addTab;

  @T_srv.ref('mainTabs') readonly tabsRef!: any;

  @T_srv.ref('chgPwdForms') readonly formRef!: any;

  @T_srv.watch('sysMsgCount', { deep: true })
  watchMsgCount() {
    this.listenMsg(false);
  }

  // 父组件compBoxInfo
  @T_srv.prop({
    required: false,
    type: Object,
    default: null
  })
  compBoxInfo: ifs.I_compBoxInfo;

  private pwdRegex: RegExp = new RegExp(
    '(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{8,30}'
  );

  private pwdLengthRegex: RegExp = /^.{8,}$/;

  private GT_compBoxInfo: ifs.I_compBoxInfo;

  private GT_compBox: ifs.I_compBox;

  private G_compID: string;

  private GT_home: T_home;

  [x: string]: any;

  private isLogOut: boolean = true;

  private version: string = '0.0';

  private versionShow: boolean = false;

  private passwordShow: boolean = false;

  private versionInfo: any = Tsrv.getSysVersion();

  public tipsShow: boolean = false;

  // 弹窗测试
  private isHasDialog: boolean = false;

  /**
   * 导航栏过渡动画
   */
  private navLeftPanelWidth: number = 160;

  // 左导航栏宽度
  public navLeftPanelStyle = {
    width: `width:${this.navLeftPanelWidth}px`
  };

  private navLeftPanelOpen: boolean = true;

  private navZoomBtnStyle = {
    transform: 'rotate(0)'
  };

  /**
   * 页签管理
   */
  editableTabsValue: string = 'index';

  public editableTabs: N_home.tabFields[] = [];

  private initTabs: N_home.tabFields = {
    // title: "主页",
    title: 'menu.homePage',
    field: 'index',
    isEdit: false,
    funID: ''
  };

  /**
   * 配置信息
   */
  private navConfig: Array<N_home.navRootEle> = [];

  private messageConfig: any = {};

  public layoutConfig: any = {};
  //  <!-- 移到app.vue -->
  // public modalList: any = [];

  private historyConfig: N_home.HistoryConfig = {
    historyOpen: []
  };

  private funcConfig: I_funcConf;

  private chgPwdForm: any = {
    oldPwd: '',
    newPwd: '',
    confirmPwd: ''
  };

  private chgPwdRules: any = {
    oldPwd: [
      // { required: true, message: 'Please input password', trigger: 'blur' },
      {
        required: true,
        // validator: this.oldPwdValidate,
        validator: this.pwdValidate,
        trigger: 'blur'
      }
    ],
    newPwd: [
      {
        required: true,
        // validator: this.newPwdValidate,
        validator: this.pwdValidate,
        trigger: 'blur'
      }
    ],
    confirmPwd: [
      {
        required: true,
        validator: this.confirmPwdValidate,
        trigger: 'blur'
      }
    ]
  };

  private flag: any = {
    oldPwd: false,
    newPwd: false,
    confirmPwd: false
  };

  private PPlayoutRecord: { path: string; value: string }[];

  private layoutInfo = {};

  get tabsTitle() {
    return (field: string, title: string): string => {
      return field === 'index'
        ? Tsrv.getI18nByPath(title)
        : Tsrv.getI18nBySrv(title);
    };
  }

  get isIndex() {
    return (field: string): boolean => {
      return !(field === 'index');
    };
  }

  created() {
    this.G_compID = Tsrv.utils.getGUID();
    // 如果外部有传入组件信息
    if (Tsrv.utils.isEmpty(this.compBoxInfo)) {
      this.GT_compBox = new ts.T_compBox(this.G_compID); // 使用本地实例
    } else {
      this.G_compID = this.compBoxInfo.compID;
      this.GT_compBox = this.compBoxInfo.TcompBox;
    }
    this.GT_compBoxInfo = { compID: this.G_compID, TcompBox: this.GT_compBox };

    this.GT_home = new T_home();
    this.funcConfig = this.GT_home.funcConf;

    this.GT_compBox.addCompMergeName(this.G_compID, `home`, this);
    // 暴露引用到外部
    Tsrv.compBox.addCompMergeName(Tsrv.compBox.getDefGUID(), 'home', this);

    this.navConfig = this.$store.getters.SysInfo.navs || [];

    this.navConfig = Tsrv.utils.sortBy(this.navConfig, 'sn');
    Tsrv.web.connect(
      Tsrv.noValue(process.env.VUE_APP_WS_URL) ||
        process.env.NODE_ENV === 'production'
        ? this.$config.webSocketURL
        : process.env.VUE_APP_WS_URL
    );
  }

  destroyed() {
    // 如果父组件传入的compBoxInfo则删除本文件产生的引用，反之是本组件产生的ID统一删除
    if (Tsrv.utils.isEmpty(this.compBoxInfo)) {
      this.GT_compBox.removeCompsWithCompID(this.G_compID);
    } else {
      this.GT_compBox.removeCompsMergeName(this.G_compID, ['home']);
    }

    Tsrv.compBox.removeCompMergeName(Tsrv.compBox.getDefGUID(), 'home');
  }

  mounted() {
    Tsrv.TPP.PPInit().catch(err => {
      this.$msg(err);
    });
    this.editableTabs.push(this.initTabs);
    this.initBus();
    this.$emit('compBoxInfo', this.GT_compBoxInfo);
    this.$nextTick(() => {
      Tsrv.startTask();
    });
    this.listenMsg();
  }

  public refreshLayout(request: { name: string; config: any }) {
    const storedLayout = this.$store.getters.LayoutConfigs || {};
    if (storedLayout[request.name]) {
      this.$store
        .dispatch('getLayoutConfigAsync', {
          funid: null,
          field: request.name,
          type: 'layout'
        })
        .then(res => {
          if (res) {
            this.layoutConfig[request.name] = Tsrv.utils.clone(
              res.current,
              true
            );
          }
        });
    }
  }

  public refreshNav() {
    Tsrv.standardPostReq<any, any>({}, Tsrv.lexicon.url.getNav).then(
      ([res, err]) => {
        !err && this.$msg(err);
        res.success && (this.navConfig = Tsrv.utils.sortBy(res.data, 'sn'));
      }
    );
  }

  /**
   * 页签
   */
  public addTab(field: string) {
    // 报表设计
    if (field === 'report_design') {
      window.open(
        `${this.$config.reportURL}list?token=${Tsrv.cookies.getToken()}`
      );
      return undefined;
    }
    return new Promise(resolve => {
      // 调用getfun加载到缓存并取得配置this.layoutConfig
      this.$store
        .dispatch('getLayoutConfigAsync', {
          field,
          type: 'layout'
        })
        .then(res => {
          if (res) {
            // 获取配置
            this.layoutConfig = res.all;
            this.doAddTab(field);
            this.$nextTick(() => {
              resolve(this.getCurrentTab());
            });
          } else {
            resolve(false);
          }
        });
    });
  }

  private getCurrentTab() {
    const index: number = Tsrv.utils.findIndexOf(
      this.editableTabs,
      el => el.field === this.editableTabsValue
    );
    if (index !== null) {
      return this.tabsRef.getTabs()[index] || null;
    }
    return null;
  }

  public doAddTab(field: string) {
    // 存入历史开启功能
    this.addHistory(field, Tsrv.getI18nBySrv(field));
    // 功能没有开启
    if (!this.editableTabs.find(item => item.field === field)) {
      this.editableTabs.push({
        title: field,
        field,
        isEdit: false,
        funID: Tsrv.getMainFunInfoByFunName(field)?.mainFunID
      });
    }
    // 将用户喜好的layout数据做对应，然后修改layoutConfig,
    this.PPlayoutRecord = Tsrv.TPP.getLayoutByMainFun(field);
    if (!Tsrv.noValue(this.PPlayoutRecord)) {
      this.PPlayoutRecord.forEach(item => {
        if (
          !this.setObjValByPath(this.layoutConfig[field], item.path, item.value)
        ) {
          // 不存在的路径，即该功能被删除或被修改，则删除该记录。否则，该记录会一直存在，导致布局错乱
          Tsrv.TPP.removeLayoutByMainFun(field);
        }
      });
    }
    // 记录一份新的layoutconfig数据，与removeTab时作对比
    const newLayoutCfs = {};
    for (const key in this.layoutConfig) {
      newLayoutCfs[key] = {};
      this.getLayoutInfo(this.layoutConfig[key], newLayoutCfs[key]);
    }
    this.layoutInfo = newLayoutCfs;
    // 定位当前功能
    this.editableTabsValue = field;
  }

  // 获取布局信息
  private getLayoutInfo(layoutConfig, newLayoutCf) {
    if (layoutConfig.name === 'BaseTab') {
      newLayoutCf.config = {};
      if (Tsrv.noValue(layoutConfig.config.panels)) {
        this.getLayoutInfo(layoutConfig.config, newLayoutCf.config);
      } else {
        newLayoutCf.config.panels = [];
        layoutConfig.config.panels.forEach((item: any, index: number) => {
          newLayoutCf.config.panels[index] = {};
          newLayoutCf.config.panels[index].content = {};
          this.getLayoutInfo(
            item.content,
            newLayoutCf.config.panels[index].content
          );
        });
      }
    } else {
      if (layoutConfig.type === 'layout') {
        newLayoutCf.config = {};
        newLayoutCf.config.value = layoutConfig.config.value;
        newLayoutCf.config.first = {};
        this.getLayoutInfo(layoutConfig.config.first, newLayoutCf.config.first);
        newLayoutCf.config.second = {};
        this.getLayoutInfo(
          layoutConfig.config.second,
          newLayoutCf.config.second
        );
      }
    }
    // return newLayoutCf;
  }

  public beforeRemoveTab(idx: number) {
    return new Promise((resolve, reject) => {
      const isEdit: boolean | number = this.editableTabs[idx].isEdit;

      if (isEdit) {
        this.$Modal.confirm({
          // title: "关闭",
          // content: "该页签未编辑完成，是否关闭？",
          title: Tsrv.getI18nByPath('others.closed') as string,
          content: Tsrv.getI18nByPath('omagicbox.isclosed') as string,
          onOk: () => {
            resolve(true);
            this.cleanFunLock(this.editableTabs[idx].funID);
          },
          onCancel: () => {
            reject(false);
          }
        });
      } else {
        this.cleanFunLock(this.editableTabs[idx].funID);
        resolve(true);
      }
    });
  }

  private cleanFunLock(funID: string) {
    setTimeout(() => {
      const { d, ok } = Tsrv.TlockInfo.getLockInfoByfunID(funID);
      if (ok) {
        d.forEach(ele => {
          this.apiEditLock({
            funID: ele.funID,
            rowID: ele.rowID,
            key: ele.key,
            cmd: 'unLock'
          }).then(([res, err]) => {
            if (err) {
              this.$msg(err);
            }
          });
        });
      }
      Tsrv.TlockInfo.removeLockInfoByFunID(funID);
    }, 1);
  }

  private apiEditLock(p: {
    funID: string;
    cmd: 'lock' | 'unLock';
    rowID: string;
    key: string;
  }): Promise<[ifs.I_setDataRes, string]> {
    return Tsrv.standardPostReq<ifs.I_setDataRes, any>(p, Tsrv.url.editLock);
  }

  public removeTab(targetName: string) {
    const tabs: N_home.tabFields[] = this.editableTabs;
    let activeName: string = this.editableTabsValue;
    const isEdit: number | boolean = Tsrv.utils.find(
      tabs,
      el => el.field === targetName
    ).isEdit;

    if (activeName === targetName) {
      tabs.forEach((tab, index) => {
        if (tab.field === targetName) {
          const nextTab: N_home.tabFields = tabs[index + 1] || tabs[index - 1];
          nextTab && (activeName = nextTab.field);
        }
      });
    }
    this.editableTabsValue = activeName;
    this.editableTabs = tabs.filter(tab => tab.field !== targetName);
    // 退出功能时，删除全局主功能comp引用
    Tsrv.compBox.removeCompMergeName(Tsrv.compBox.getDefGUID(), targetName);

    // 关闭前记录用户行为
    this.reBehaviorBeforeClose([targetName]);
  }

  // 关闭前记录用户行为
  private reBehaviorBeforeClose(targetNames: Array<string>) {
    // if (Array.isArray(targetNames)) {
    const layoutRecords = [];
    targetNames.forEach(fun => {
      const newLayoutInfo = {};
      this.getLayoutInfo(this.layoutConfig[fun], newLayoutInfo);
      if (
        Tsrv.utils.md5(JSON.stringify(newLayoutInfo)) !==
        Tsrv.utils.md5(JSON.stringify(this.layoutInfo[fun]))
      ) {
        const layoutInfos = this.findValuesAndPaths(newLayoutInfo);
        layoutRecords.push({
          mainFun: fun,
          layout: layoutInfos
        });
      }
    });
    if (layoutRecords.length > 0) {
      Tsrv.TPP.saveLayout(layoutRecords).catch(err => {
        this.$msg(err);
      });
    } else {
      // 无layout差异信息直接保存该功能行为数据
      Tsrv.TPP.saveBehaviorData().catch(err => {
        this.$msg(err);
      });
    }
  }

  private findValuesAndPaths(
    obj,
    path = ''
  ): { path: string; value: string }[] {
    let results = [];
    // 遍历对象的所有属性
    for (const key in obj) {
      // if (obj.hasOwnProperty(key)) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        const newPath = path ? `${path}.${key}` : key; // 更新路径
        // 如果当前属性是一个对象，则递归调用
        if (typeof obj[key] === 'object' && obj[key] !== null) {
          results = results.concat(this.findValuesAndPaths(obj[key], newPath));
        }
        // 如果当前属性是我们要找的value字段
        else if (key === 'value') {
          results.push({ path: newPath, value: obj[key] });
        } else if (Array.isArray(obj[key])) {
          obj[key].forEach((item, index) => {
            if (typeof item === 'object' && item !== null) {
              // 对于数组中的对象，路径需要包含索引
              results = results.concat(
                this.findValuesAndPaths(item, `${newPath}[${index}]`)
              );
            }
          });
        }
      }
    }
    return results;
  }

  public changeTab(targetName: string) {
    const tabs: N_home.tabFields[] = this.editableTabs;
    const activeName: string = this.editableTabsValue;
    let field: string = '';
    if (activeName !== targetName) {
      tabs.forEach(tab => {
        if (tab.field === targetName) {
          field = targetName;
        }
      });
    }
    if (field !== '') {
      this.addHistory(field, Tsrv.getI18nBySrv(field));
    }
  }

  public navZoom() {
    if (this.navLeftPanelOpen) {
      this.navLeftPanelStyle = {
        width: '50px'
      };
      this.navZoomBtnStyle = {
        transform: 'rotate(90deg)'
      };
      this.$bus.$emit('navZoom', true);
      this.navLeftPanelOpen = false;
    } else {
      this.navLeftPanelStyle = {
        width: '160px'
      };
      this.navZoomBtnStyle = {
        transform: 'rotate(0)'
      };
      this.$bus.$emit('navZoom', false);
      this.navLeftPanelOpen = true;
    }
  }

  /**
   * 顶部功能
   */
  public addHistory(title: string, text: string) {
    const value = `history|${title}`;
    const open: N_home.History[] = this.historyConfig.historyOpen;
    const historyEle: N_home.History = {
      value,
      text
    };
    const find = Tsrv.utils.findIndexOf(open, el => el.value === value);
    find === -1 && open.unshift(historyEle);
  }

  // 功能选择
  public selectFunc(val: string) {
    if (val && val.indexOf('|') > -1) {
      const arr: string[] = val.split('|');
      const type: string = arr[0];
      const fun: string = arr[1];
      switch (type) {
        case 'history': // 历史
          this.addTab(fun);
          break;
        case 'theme': // 主题
          setTheme(fun);
          break;
        case 'errList':
          this.showErrDet(fun);
          break;
        case 'todo': // 待办事项
          // this.isMsgCount(type, fun)
          //   ? this.chgTodo(fun)
          //   : this.$Message.info(Tsrv.getI18nByPath('others.noinformation'));
          this.showMsgList(tvs.V_msgType.TO_DO_LIST, fun);
          break;
        case 'notify': // 通知
          // this.chgNotify(fun);
          this.showMsgList(tvs.V_msgType.NOTICE, fun);
          break;
        case 'broadcast': // 广播
          this.showMsgList(tvs.V_msgType.BROADCAST, fun);
          break;
        case 'alarm': // 警报
          this.showMsgList(tvs.V_msgType.ALARM, fun);
          break;
        case 'earlyWarning': // 预警
          this.showMsgList(tvs.V_msgType.EARLY_WARNING, fun);
          break;
        case 'help':
          this.doHelp(fun);
          break;
        case 'warnmsg':
          this.clickWarnMsg(fun);
          break;
        case 'admsg':
          this.clickAdMsg(fun);
          break;
        case 'user':
          this.clickchgPwd(fun);
          break;
        default:
          throw new Error(`err`);
      }
    }
  }

  clickWarnMsg(P_fun: string) {
    this.addTab('SO_WarnMessage');
  }

  clickAdMsg(P_fun: string) {
    this.addTab('SO_AdvertPublish');
  }

  clickchgPwd(P_fun: string) {
    this.passwordShow = true;
  }

  showMsgList(msgType: tvs.V_msgType, category: string) {
    if (!Tsrv.noValues(msgType, category)) {
      const typeName = Tsrv.wsCommunicate.getMsgType(msgType);
      const filter = new Map();
      filter.set(Tsrv.globalVar.fieldName.msgType, new Set([msgType]));
      const visibility = new Map();
      visibility.set(Tsrv.globalVar.fieldName.msgCategory, new Set([category]));
      const cfg = {
        modalSize: [560, 420],
        modalTitle: Tsrv.getI18nByPath(`menu.${typeName}.title`),
        config: { filter, visibility }
      };
      Tsrv.utils.createModal('o-msg-list', cfg).then(res => {});
    } else {
      this.$Message.info(Tsrv.getI18nByValue('无效的消息分类'));
    }
  }

  // 帮助
  public doHelp(P_fun: string) {
    switch (P_fun) {
      case 'helpFile':
        window.open(
          `${this.$config.documentURL}${Tsrv.url.getUserFilePath}`,
          '_blank'
        );
        break;
      case 'developFile':
        window.open(
          `${this.$config.documentURL}${Tsrv.url.getDeveloperFilePath}`,
          '_blank'
        );
        break;
      case 'versionInfomation':
        this.versionShow = !this.versionShow;
        break;
      default:
        break;
    }
  }

  /**
   * @description: 整理消息到store中
   * @param {*} msgInfo
   * @return {*}
   * @author: swc
   * @Date: 2024-12-17 17:36:13
   * @LastEditors: swc
   * @LastEditTime: Do not edit
   */
  public handleMsgCount(msgInfo: any) {
    const { type, category, status } = msgInfo;
    const state = Tsrv.noValue(status) ? 0 : status;
    // 修改消息统计（存store中）———— 旧版
    Tsrv.wsCommunicate.msgAddStoreCount(type, category, state);
    // 修改消息统计（存store中）———— 新版
    Tsrv.wsCommunicate.handleMsgStoreCount(type, category, state);

    // 判断是否打开 右下角消息弹窗
    if (status === 0 && !this.tipsShow) {
      this.tipsShow = true;
    }
  }

  // 单据审核
  public chgTodo(name: string) {
    if (name === 'billAudit') {
      Tsrv.utils
        .createModal('msg-todo-audit', {
          // modalTitle: "待审核业务",
          modalTitle: Tsrv.getI18nByPath('others.pendingbusiness'),
          modalSize: [700, 500]
        })
        .then(data => {
          if (data) {
            const msg: any = {
              type: 'requestFilter',
              val: [
                {
                  field: 'id',
                  value: data.page
                }
              ]
            };
            this.addTab(data.name).then(res => {
              setTimeout(() => {
                this.$bus.$emit(data.name, msg);
              }, 0);
            });
          }
        });
    }
  }

  // 通知
  public chgNotify(name: string) {
    if (name === 'notice') {
      const find = Tsrv.utils.find(
        this.funcConfig.notify.el,
        el => el.value === 'notify|notice'
      );
      Tsrv.utils
        .createModal('msg-notify-notice', {
          // modalTitle: "通知公告",
          modalTitle: Tsrv.getI18nByPath('menu.notice.announcement'),
          modalSize: [700, 500],
          count: find.dot
        })
        .then(data => {
          find.dot = data;
        });
    }
  }

  // 错误
  public chgErrList() {
    const errs = Tsrv.TerrDet.getList();
    this.funcConfig.errList.config.dot = errs.length;
  }

  // 判断消息是否为空
  public isMsgCount(type: string, fun: string): boolean {
    let count: number = null;
    count = +Tsrv.utils.find(
      this.funcConfig[type].el,
      el => el.value === `${type}|${fun}`
    ).dot;
    return count > 0;
  }

  /**
   * @Author: ssj
   * @description: 初始化总线
   */
  public initBus() {
    // 刷新结构
    this.$bus.$on('OPEN_URL', url => {
      window.open(url, '_blank');
    });

    // 刷新结构
    this.$bus.$on('refreshLayout', (request: { name: string; config: any }) => {
      this.refreshNav();
      this.refreshLayout(request);
    });

    // 更新token
    this.$bus.$on('__REFRESH_TOKEN__', (data: any) => {
      console.info('refresh token:', data);
      Tsrv.cookies.setToken(data.token);
      this.$store.commit('SET_TOKEN', data.token);
    });

    // 接受消息
    this.$bus.$on('__ALARM__', (data: any) => {
      const msgInfo = {
        // cmd: data.cmd,
        type: data.m.t,
        category: data.m.d.category
      };
      this.handleMsgCount(msgInfo);
    });
    this.$bus.$on('__BROADCAST__', (data: any) => {
      const msgInfo = {
        // cmd: data.cmd,
        type: data.m.t,
        category: data.m.d.category
      };
      this.handleMsgCount(msgInfo);
    });
    this.$bus.$on('__EARLY_WARNING__', (data: any) => {
      const msgInfo = {
        // cmd: data.cmd,
        type: data.m.t,
        category: data.m.d.category
      };
      this.handleMsgCount(msgInfo);
    });
    this.$bus.$on('__NOTICE__', (data: any) => {
      const msgInfo = {
        // cmd: data.cmd,
        type: data.m.t,
        category: data.m.d.category
      };
      this.handleMsgCount(msgInfo);
    });
    this.$bus.$on('__TO_DO_LIST__', (data: any) => {
      const msgInfo = {
        // cmd: data.cmd,
        type: data.m.t,
        category: data.m.d.category
      };
      this.handleMsgCount(msgInfo);
    });
    this.$bus.$on('__WS_SERVER_INFO__', (data: any) => {
      this.handleWsServerInfo(data);
    });
    this.$bus.$on('__WS_SUBSCRIPTION__', (data: any) => {
      // console.log('ws subscription', data);
    });

    // 监听basegrid编辑状态
    this.$bus.$on(
      'bus_basegrid_editStatus',
      (isEdit: boolean, funcName: string) => {
        this.changeTabsClose(isEdit, funcName);
      }
    );

    // ws 消息展示后，先点击之后 回调
    // 监听一些消息 需要整合修改
    // this.$bus.$on('chgTodoFunc', (fun: string) => {
    //   this.chgTodo(fun);
    // });

    // this.$bus.$on('chgNotifyFunc', (fun: string) => {
    //   this.chgNotify(fun);
    // });
    this.$bus.$on('chgErrListFunc', () => {
      this.chgErrList();
    });
  }

  private handleWsServerInfo(data: any) {
    // clientID
    const connCliID = data?.m?.d?.connCliID;
    this.$store.commit('SET_CONNECT_CLIENTID', connCliID);
  }

  // 监听：当收到消息时
  private async listenMsg(isSync: boolean = true) {
    const [b, res, rows, err] = await this.loadMyMsgs(
      '_sys_msg',
      '_sys_msg',
      Tsrv.globalVar.dbType.MYSQL
    );
    if (b) {
      rows.forEach((item: any) => {
        const content = JSON.parse(item[Tsrv.globalVar.fieldName.msgContent]);
        const msgInfo = {
          type: item[Tsrv.globalVar.fieldName.msgType],
          category: content?.category,
          status: item[Tsrv.globalVar.fieldName.msgStatus]
        };
        this.handleMsgCount(msgInfo);
      });
    } else {
      console.error(Tsrv.getI18nByValue('获取消息失败'));
    }
  }

  public loadMyMsgs(funName: string, tableName: string, dbType: string) {
    const gpd = new ts.T_getPartDataArg();
    const filter = {
      // f_status: { $in: [0] },
      user_no: { $in: [Tsrv.getUserNo()] }
    };
    gpd.setFilter(filter);
    gpd.setFunName(funName);
    gpd.setTableName(tableName);
    gpd.setDbType(dbType);
    gpd.setLang(this.$i18n.locale);
    gpd.setPage(1, 99999);
    gpd.setRule({} as ifs.I_rule);
    return Tsrv.getData(gpd);
  }

  /**
   * @Author: ssj
   * @description: 右键页签
   * @param {any} data 右键选中的页签
   */
  public handleContextMenu(data: any) {
    this.editableTabsValue = data.name;
  }

  /**
   * @Author: ssj
   * @description: 关闭全部页签
   */
  public tabCloesOther(): void {
    const data: N_home.tabFields[] = [];
    data.push(
      this.initTabs,
      Tsrv.utils.find(
        this.editableTabs,
        el => el.field === this.editableTabsValue
      ),
      ...Tsrv.utils.filter(
        this.editableTabs,
        el => el.isEdit && el.field !== this.editableTabsValue
      )
    );

    // 退出功能时，保存该功能行为数据
    const funNames = this.editableTabs.reduce((prev, fun) => {
      if (
        fun.field !== 'index' &&
        !fun.isEdit &&
        fun.field !== this.editableTabsValue
      ) {
        prev.push(fun.field);
      }
      return prev;
    }, []);
    if (funNames.length > 0) {
      setTimeout(() => {
        this.reBehaviorBeforeClose(funNames);
      }, 100);
    }

    this.editableTabs = data;
  }

  /**
   * @Author: ssj
   * @description: 关闭左侧页签
   */
  public tabCloesLeft(): void {
    const index: number = Tsrv.utils.findIndexOf(
      this.editableTabs,
      el => el.field === this.editableTabsValue
    );
    const left: N_home.tabFields[] = this.editableTabs.splice(1, index - 1);
    this.editableTabs.splice(
      1,
      0,
      ...Tsrv.utils.filter(
        left,
        el => el.isEdit && el.field !== this.editableTabsValue
      )
    );

    // 退出功能时，保存该功能行为数据
    const funNames = left.reduce((prev, fun) => {
      if (!fun.isEdit && fun.field !== this.editableTabsValue) {
        prev.push(fun.field);
      }
      return prev;
    }, []);
    if (funNames.length > 0) {
      setTimeout(() => {
        this.reBehaviorBeforeClose(funNames);
      }, 100);
    }
  }

  /**
   * @Author: ssj
   * @description: 关闭右侧页签
   */
  public tabCloesRight(): void {
    const index: number = Tsrv.utils.findIndexOf(
      this.editableTabs,
      el => el.field === this.editableTabsValue
    );
    const right: N_home.tabFields[] = this.editableTabs.splice(
      index + 1,
      this.editableTabs.length - index
    );
    this.editableTabs.push(
      ...Tsrv.utils.filter(
        right,
        el => el.isEdit && el.field !== this.editableTabsValue
      )
    );

    // 退出功能时，保存该功能行为数据
    const funNames = right.reduce((prev, fun) => {
      if (!fun.isEdit && fun.field !== this.editableTabsValue) {
        prev.push(fun.field);
      }
      return prev;
    }, []);
    if (funNames.length > 0) {
      setTimeout(() => {
        this.reBehaviorBeforeClose(funNames);
      }, 100);
    }
  }

  /**
   * @Author: ssj
   * @param {boolean} isEdit 编辑状态
   * @param {string} funcName 功能名
   * @description: 改变tabs的关闭状态
   */
  public changeTabsClose(isEdit: boolean, funcName: string): void {
    const find: N_home.tabFields = Tsrv.utils.find(
      this.editableTabs,
      el => el.field === funcName
    );
    find && (find.isEdit = isEdit);
  }

  // public handleDragDrop(
  //   name: string,
  //   newName: string,
  //   a: number,
  //   b: number,
  //   names: string[]
  // ) {
  //   this.editableTabs.splice(
  //     b,
  //     1,
  //     ...this.editableTabs.splice(a, 1, this.editableTabs[b])
  //   );
  // }

  public async chgOk() {
    this.formRef.validate(valid => {
      if (valid) {
        const roldP = Tsrv.utils.EnCode(this.chgPwdForm.oldPwd);
        if (roldP.isErr()) {
          this.$msg(Tsrv.getI18nByMsg(roldP.getErrMsg()));
          return;
        }
        const oldP = roldP.getReturnData('data');

        const rnewP = Tsrv.utils.EnCode(this.chgPwdForm.newPwd);
        if (rnewP.isErr()) {
          this.$msg(Tsrv.getI18nByMsg(rnewP.getErrMsg()));
          return;
        }
        const newP = rnewP.getReturnData('data');

        const params = { oldPWD: oldP, newPWD: newP };
        Tsrv.standardPostReq<ifs.I_setDataRes, any>(
          params,
          Tsrv.lexicon.url.resetPWD
        ).then(([res, msg]) => {
          // Tsrv.httpReqV1.resetPWD(oldP, newP).then(res => {
          this.$msg(msg ?? res.msg);
          if (res.success) {
            this.exitForm();
            Tsrv.cookies.removeCookies();
            this.$router.push({ path: Tsrv.lexicon.routerPath.login.path });
          }
        });
      }
    });
  }

  public chgCanel() {
    this.exitForm();
  }

  public exitForm() {
    this.formRef.resetFields();
    // Tsrv.cookies.removeToken();
    // this.close();
    this.passwordShow = false;
  }

  // 当打开一个主功能时将实例注册到Tsrv,主功能名是全局唯一的，方便主功能间通讯
  public openFun(mainFunName: string, e: ifs.I_compBoxInfo) {
    const obj = e.TcompBox.findCompByFunName(mainFunName);
    if (!Tsrv.utils.noValue(obj)) {
      Tsrv.compBox.addCompMergeName(
        Tsrv.compBox.getDefGUID(),
        mainFunName,
        obj
      );
    }
  }

  private oldPwdValidate(rule, value, callback) {
    if (value === '') {
      callback(
        new Error(
          Tsrv.getI18nByPath('system.interface.formRule.oldPasswordIsRequired')
        )
      );
    } else {
      callback();
    }
  }

  private newPwdValidate(rule, value, callback) {
    if (value === '') {
      callback(
        new Error(
          Tsrv.getI18nByPath('system.interface.formRule.newPasswordIsRequired')
        )
      );
    } else if (!this.pwdRegex.test(value)) {
      callback(
        new Error(Tsrv.getI18nByPath('system.interface.formRule.PWDComplexity'))
      );
    } else {
      callback();
    }
  }

  private confirmPwdValidate(rule, value, callback) {
    if (value === '') {
      callback(
        new Error(
          Tsrv.getI18nByPath(
            'system.interface.formRule.confirmPasswordIsRequired'
          )
        )
      );
    } else if (value !== this.chgPwdForm.newPwd) {
      callback(
        new Error(
          Tsrv.getI18nByPath('system.interface.formRule.passwordsNotMatch')
        )
      );
    } else if (!this.pwdLengthRegex.test(value)) {
      callback(
        new Error(
          Tsrv.getI18nByPath('system.interface.formRule.passwordsNotMatch')
        )
      );
    } else if (!this.pwdRegex.test(value)) {
      callback(
        new Error(Tsrv.getI18nByPath('system.interface.formRule.PWDComplexity'))
      );
    } else {
      callback();
    }
  }

  private pwdValidate(rule, value: string, callback) {
    if (value.trim() === '') {
      callback(
        new Error(
          Tsrv.getI18nByPath('system.interface.formRule.passwordsCannotBeEmpty')
        )
      );
    } else if (!this.pwdLengthRegex.test(value)) {
      callback(
        new Error(
          Tsrv.getI18nByPath('system.interface.formRule.passwordsLengthPrompt')
        )
      );
    } else if (!this.pwdRegex.test(value)) {
      callback(
        new Error(Tsrv.getI18nByPath('system.interface.formRule.PWDComplexity'))
      );
    } else {
      callback();
    }
  }

  private showErrDet(fun: string) {
    if (fun === 'currentErr') {
      const eList = Tsrv.TerrDet.getList();
      if (eList.length > 0) {
        Tsrv.dialog.confirm(eList[0].content, eList[0].title, {
          status: 'error'
        });
      }
    }
  }

  // 根据路径设置对象的值，如果路径不存在则返回false
  private setObjValByPath(obj: any, path: string, value: any): boolean {
    const keys = path.split('.'); // 将路径拆分为键数组
    // 递归函数，用于在对象中设置路径对应的值
    function setValue(o: any, ks: string[], val: any): boolean {
      if (ks.length === 1) {
        // 如果当前键不存在，则创建一个空对象
        if (Tsrv.noValue(o[ks[0]])) return false;
        o[ks[0]] = val; // 已经遍历完路径，设置最终值
        return true;
      }

      const key = ks.shift(); // 取出第一个键
      if (o && typeof o === 'object' && key in o) {
        return setValue(o[key], ks, val); // 继续递归设置下一个键
      }
      return false; // 如果路径不存在，返回false
    }
    return setValue(obj, keys, value);
  }
}
</script>

<style lang="scss" scoped>
/* 整体布局 */
.container {
  overflow: hidden;
  width: 100%;
  height: 100%;
  min-width: 768px;
  display: flex;
  flex-direction: row;
  .right-panel {
    position: relative;
    height: 100%;
    flex-grow: 1;
    .main-panel {
      position: absolute;
      left: 0;
      top: 48px;
      width: 100%;
      height: calc(100% - 48px);
      margin: 0;
      overflow: auto;
      .ivu-tabs .ivu-tabs-content .ivu-tabs-tabpane {
        box-shadow: none;
      }
    }
  }

  .version-box {
    height: 100%;
    width: 100%;
    position: absolute;
    display: flex;
    justify-content: center;
    z-index: 999;
    // background-color: var(--input-phcolor); //#80808040
    background-color: #80808040; //#80808040

    .info-container {
      background-color: #fff;
      width: 350px;
      height: initial !important;
      max-height: 60vh;
      padding: 0 30px;
      position: absolute;
      top: 20%;
      box-shadow: 0 0 22px var(--default-box-shadow-down);

      border-radius: 5px;
      .version-title {
        font-size: 16px;
        font-weight: bold;
        margin: 10px 0;
        text-align: center;
        padding: 10px;
        border-bottom: 1px solid var(--default-border-color);
      }
      .info-scoll {
        // height: 65%;

        .version-item {
          display: flex;
          justify-content: space-between;
          margin-bottom: 10px;
          line-height: 2.5;
          > span {
            display: block;
            flex: 2;
            white-space: nowrap;
            text-align: flex-end;
            margin-right: 20px;
          }
          div {
            flex: 7;
            white-space: nowrap;
          }
        }
      }
      .version-footer {
        display: flex;
        justify-content: flex-end;
        align-items: center;
        margin-bottom: 20px;
      }
    }
  }
  .password-box {
    background: #fff;
    position: absolute;
    top: 50px;
    right: 20px;
    // z-index: 103;
    z-index: 203;
    border: 1px solid #e4e7ed;
    width: 225px;
    height: 260px;
    padding: 6px;
    // background-color: pink;
    .chgPwd-button {
      padding: 10px;
      margin-top: 6px;
      border-top: 1px solid #e4e7ed;
      ::v-deep .ivu-btn {
        margin-left: 38px;
      }
    }
    ::v-deep .ivu-form {
      margin-left: 10px;
    }
    ::v-deep .ivu-form-item {
      margin-bottom: 10px;
      margin-right: unset;
    }
  }
}
.left-panel {
  width: 160px;
  height: 100%;
  transition: ease-in-out 0.1s;
  background-color: var(--nav-bg-color);
  transition: var(--animation-themechange);
  border-right: 1px solid #ddd;
  .nav-title {
    width: 100%;
    height: 48px;
    overflow: hidden;
    position: relative;
    transition: var(--animation-themechange);
    background-color: var(--nav-title-bg-color);
    border-bottom: 1px solid #ddd;
    .logo {
      position: absolute;
      height: 100%;
      width: calc(100% - 48px);
      right: 0;
      top: 0;
      span {
        font-size: 18px;
        font-style: oblique;
        color: var(--nav-title-font-color);
        line-height: 48px;
        display: block;
        width: 100%;
      }
    }
    .logo-elip {
      height: 36px;
      width: 36px;
      margin: 7px;
      cursor: pointer;
      transition: var(--animation-themechange);
      border-radius: 100%;
      i {
        font-size: 24px;
        color: var(--nav-title-font-color);
        margin: 6px;
      }
    }
    .logo-elip:hover {
      background-color: var(--nav-title-hover-bg-color);
    }
  }
  .nav-content {
    max-height: calc(100% - 48px);
    width: 100%;
    overflow-x: visible;
    overflow-y: auto;
    ul {
      padding: 10px 0;
      li {
        transition: var(--animation-hover);
      }
    }
  }
  .nav-content::-webkit-scrollbar {
    /* 滚动条的宽和高 */
    width: 8px;
    height: 0px;
  }
  .nav-content::-webkit-scrollbar-thumb {
    /*滚动条里面小方块*/
    border-radius: 4px;
    box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.2);
    background: rgba(0, 0, 0, 0.2);
  }
}
</style>
